﻿using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Net;

namespace SZJT.Framework.Adapter
{
    internal class SocketClient
    {
        private const int _clientPause = 10;
        private string _remoteAddress = string.Empty;
        private int _remotePort = 0;
        private int _maxDataSize = 10 * 1024 * 1024;
        private int _readTimeout = 30;
        private Exception _exception = null;
        private System.Net.Sockets.Socket _socketClient = null;
        private object _lock = new object();
        public string ClientIP { get; set; }
      

        internal SocketClient(string remoteAddress, int remotePort)
        {
            if (remoteAddress != null) _remoteAddress = remoteAddress;
            if (remotePort > 0) _remotePort = remotePort;
        }
        internal SocketClient(string remoteAddress,string clientIP, int remotePort)
        {
            if (remoteAddress != null) _remoteAddress = remoteAddress;
            if (remotePort > 0) _remotePort = remotePort;
            ClientIP = clientIP;
        }

        ~SocketClient()
        {
            Reset();
        }

        internal Exception GetLastException()
        {
            lock (_lock)
            {
                return _exception;
            }
        }

        internal void SetMaxDataSize(int maxDataSize)
        {
            if (maxDataSize >= 1024)
                _maxDataSize = maxDataSize;
        }

        internal void SetReadTimeout(int readTimeout)
        {
            lock (_lock)
            {
                if (readTimeout >= 5 & readTimeout <= 1200)
                    _readTimeout = readTimeout;
            }
        }

        internal System.Net.Sockets.Socket GetSocket()
        {
            lock (_lock)
            {
                return _socketClient;
            }
        }

        internal virtual bool Connect()
        {
            try
            {
                Monitor.Enter(_lock);
                Reset();
                _socketClient = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint end = null;
                try
                {
                    end = (_remoteAddress == string.Empty) ? (new IPEndPoint(Dns.GetHostEntry(Dns.GetHostName()).AddressList[0], _remotePort))
                        : (new IPEndPoint(IPAddress.Parse(_remoteAddress), _remotePort));
                }
                catch { }
                if (end == null)
                {
                    end = new IPEndPoint(Dns.GetHostEntry(Dns.GetHostName()).AddressList[0], _remotePort);
                }
                _socketClient.Connect(end);

                return true;
            }
            catch (Exception ex)
            {
                _exception = ex;
                try
                {
                    _socketClient.Shutdown(SocketShutdown.Both);
                    _socketClient.Close();
                }
                catch { }
                return false;
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        internal bool SendData(string data)
        {
            byte[] data2 = GetDataByte(data);

            try
            {
                Monitor.Enter(_lock);
                return _socketClient.Send(data2) == data2.Length;
            }
            catch (Exception ex)
            {
                Connect();
                _exception = ex;
                return false;
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        private byte[] GetDataByte(string data)
        {
            byte[] data2 = new byte[Encoding.UTF8.GetByteCount(data) + 4];
            Encoding.UTF8.GetBytes(data, 0, data.Length, data2, 4);
            byte[] dataLength = BitConverter.GetBytes(data2.Length - 4);
            dataLength.CopyTo(data2, 4 - dataLength.Length);

            return data2;
        }

        internal string ReceiveData()
        {
            try
            {
                Thread.Sleep(_clientPause);
                Monitor.Enter(_lock);
                _socketClient.Blocking = false;
                Byte[] header = new byte[4];
                int pos = 0;
                long start = DateTime.Now.Ticks;
                while (pos < 4)
                {
                    if (_socketClient.Available > 0)
                    {
                        pos += _socketClient.Receive(header, pos, Math.Min(_socketClient.Available, (4 - pos)), SocketFlags.None);
                    }
                    else
                    {
                        Thread.Sleep(_clientPause);
                        if (pos < 4 && ((DateTime.Now.Ticks - start) / 10000) > _readTimeout * 1000)
                            throw new Exception("Timeout while receiving incoming data");
                    }
                }

                int size = BitConverter.ToInt32(header, 0);
                Byte[] data = new byte[size];
                pos = 0;
                start = DateTime.Now.Ticks;
                while (pos < size)
                {
                    if (_socketClient.Available > 0)
                    {
                        pos += _socketClient.Receive(data, pos, Math.Min(_socketClient.Available, (size - pos)), SocketFlags.None);
                    }
                    else
                    {
                        Thread.Sleep(_clientPause);
                        if (pos < 4 && ((DateTime.Now.Ticks - start) / 10000) > _readTimeout * 1000)
                            throw new Exception("Timeout while receiving incoming data");
                    }
                }

                return Encoding.UTF8.GetString(data);
            }
            catch (Exception ex)
            {
                Connect();
                _exception = ex;
                return null;
            }
            finally
            {
                _socketClient.Blocking = true;
                Monitor.Exit(_lock);
            }
        }

        internal void Reset()
        {
            try
            {
                Monitor.Enter(_lock);
                if (_socketClient != null)
                {
                    _socketClient.Send(new byte[4] { 255, 0, 0, 0 }, SocketFlags.None);
                    _socketClient.Shutdown(SocketShutdown.Both);
                    _socketClient.Close();
                }
            }
            catch { }
            finally
            {
                _socketClient = null;
                Monitor.Exit(_lock);
            }
        }

        /// <summary>
        /// 创建GetCommand字符串
        /// </summary>
        /// <param name="userId">用户标识</param>
        /// <param name="taskGuid">应用标识</param>
        /// <param name="dataGuid">数据标识</param>
        /// <param name="dataType">数据类型</param>
        /// <returns>字符串</returns>
        internal string BuildGetCommand(string userId, string taskGuid, string dataGuid, string dataType)
        {
            return new StringBuilder()
                .Append("<Command Type=\"GetData\">")
                .AppendFormat("<UserID>{0}</UserID>", userId)
                .AppendFormat("<TaskGuid>{0}</TaskGuid>", taskGuid)
                .AppendFormat("<DataGuid>{0}</DataGuid>", dataGuid)
                .AppendFormat("<DataType>{0}</DataType>", dataType)
                .Append("</Command>")
                .ToString();
        }
        /// <summary>
        /// 创建SetCommand字符串
        /// </summary>
        /// <param name="userId">用户标识</param>
        /// <param name="taskGuid">应用标识</param>
        /// <param name="dataGuid">数据标识</param>
        /// <param name="dataType">数据类型</param>
        /// <param name="xmlData">写入数据内容</param>
        /// <returns>字符串</returns>
        internal string BuildSetCommand(string userId, string taskGuid, string dataGuid, string dataType, string xmlData)
        {
            return new StringBuilder()
                .Append("<Command Type=\"SetData\">")
                .AppendFormat("<UserID>{0}</UserID>", userId)
                .AppendFormat("<TaskGuid>{0}</TaskGuid>", taskGuid)
                .AppendFormat("<DataGuid>{0}</DataGuid>", dataGuid)
                .AppendFormat("<DataType>{0}</DataType>", dataType)
                .AppendFormat("<XmlData>{0}</XmlData>", xmlData)
                .Append("</Command>")
                .ToString();
        }
        /// <summary>
        /// 创建TransformCommand字符串
        /// </summary>
        /// <param name="userId">用户标识</param>
        /// <param name="taskGuid">应用标识</param>
        /// <param name="xmlTransform">输入参数</param>
        /// <returns>字符串</returns>
        internal string BuildTransformCommand(string userId, string taskGuid, string xmlTransform)
        {
            return new StringBuilder()
                .Append("<Command Type=\"TransformData\">")
                .AppendFormat("<UserID>{0}</UserID>", userId)
                .AppendFormat("<TaskGuid>{0}</TaskGuid>", taskGuid)
                .AppendFormat("<XmlTransform>{0}</XmlTransform>", xmlTransform)
                .Append("</Command>")
                .ToString();
        }
    }
}
